package com.example.sm2.cipher.sm2;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;

import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;

public class SM2Utils {
    /**
     * 私钥
     */
    private static final String prik = "38846090DED1E8BB60D53022975C64C01FC3F0C83F74D7A82C2B8D2D01F5FE0E";
    /**
     * 公钥
     */
    private static final String pubk = "04F84B1340330AA6D24A713EF5AFF8C1E4505161AD9FFB26F1A92C530497600550495643A923118DCAA1A55935638889C88D4263280A6C4485537150C0C8A5BA4C";

    private final static int RS_LEN = 32;

    /**
     * 公私钥生成方法
     */
    public static AsymmetricCipherKeyPair generateKeyPair(){
        SM2 sm2 = SM2.Instance();
        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
        return key;
    }

    /**
     * 获取私钥
     * @return
     */
    public static String getPrik(){
        AsymmetricCipherKeyPair keyPair = generateKeyPair();
        ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)keyPair.getPrivate();
        BigInteger privateKey = ecpriv.getD();
        return Util.byteToHex(privateKey.toByteArray());
    }
    /**
     * 获取公钥
     * @return
     */
    public static String getPubk(){
        AsymmetricCipherKeyPair keyPair = generateKeyPair();
        ECPublicKeyParameters ecpub = (ECPublicKeyParameters)keyPair.getPublic();
        ECPoint publicKey = ecpub.getQ();
        return Util.byteToHex(publicKey.getEncoded(false));
    }

    /**
     * 加密
     * @param data
     * @return
     */
    public static String encryption(String data){
        byte[] publicKey = Util.hexToByte(pubk);
        byte[] encryptionData = data.getBytes();
        String encrypt = "";
        try {
            encrypt = encrypt(publicKey, encryptionData);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return encrypt;
    }

    /**
     * sm2加密方法
     */
    public static String encrypt(byte[] publicKey, byte[] data)
            throws IOException
    {
        if ((publicKey == null) || (publicKey.length == 0))
        {
            return null;
        }

        if ((data == null) || (data.length == 0))
        {
            return null;
        }

        byte[] source = new byte[data.length];
        System.arraycopy(data, 0, source, 0, data.length);

        Cipher cipher = new Cipher();
        SM2 sm2 = SM2.Instance();
        ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);

        ECPoint c1 = cipher.Init_enc(sm2, userKey);
        cipher.Encrypt(source);
        byte[] c3 = new byte[32];
        cipher.Dofinal(c3);

        return Util.byteToHex(c1.getEncoded(false)) + Util.byteToHex(source) + Util.byteToHex(c3);
    }

    /**
     * 解密
     * @param data
     * @return
     */
    public static String decryption(String data){
        String decrypt = "";
        try {
            decrypt = decrypt(prik,data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return decrypt;
    }
    /**
     * sm2解密方法
     */
    public static String decrypt(String pk, String encryptedDataParam)
            throws IOException
    {
        if ((pk == null) || (pk.length() == 0))
        {
            return null;
        }

        if ((encryptedDataParam == null) || (encryptedDataParam.length() == 0))
        {
            return null;
        }

        byte[] privateKey = Util.hexToByte(pk);
        byte[] encryptedData = Util.hexToByte(encryptedDataParam);

        String data = Util.byteToHex(encryptedData);

        byte[] c1Bytes = Util.hexToByte(data.substring(0, 130));
        int c2Len = encryptedData.length - 97;
        byte[] c2 = Util.hexToByte(data.substring(130, 130 + 2 * c2Len));
        byte[] c3 = Util.hexToByte(data.substring(130 + 2 * c2Len, 194 + 2 * c2Len));

        SM2 sm2 = SM2.Instance();
        BigInteger userD = new BigInteger(1, privateKey);

        ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
        Cipher cipher = new Cipher();

        cipher.Init_dec(userD, c1);
        cipher.Decrypt(c2);
        cipher.Dofinal(c3);

        return new String(c2);
    }

    public static byte[] rsPlainByteArrayToAsn1(byte[] sign){
        if(sign.length != RS_LEN * 2) {
            throw new RuntimeException("err rs. ");
        }
        BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN));
        BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2));
        ASN1EncodableVector v = new ASN1EncodableVector();
        v.add(new ASN1Integer(r));
        v.add(new ASN1Integer(s));
        try {
            return new DERSequence(v).getEncoded("DER");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //    public static void main(String[] args){
//        SM2 sm2 = SM2.Instance();
//        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();
//        ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) key.getPrivate();
//        ECPublicKeyParameters ecpub = (ECPublicKeyParameters) key.getPublic();
//        BigInteger privateKey = ecpriv.getD();
//        ECPoint publicKey = ecpub.getQ();
//
//        System.out.println("公钥: " + Util.byteToHex(publicKey.getEncoded(false)));
//        System.out.println("私钥: " + Util.byteToHex(privateKey.toByteArray()));
//    }
}

